package com.yunji.storage.pattern;

import com.yunji.base.cache.BaseCache;
import com.yunji.base.domain.BelongDO;
import com.yunji.base.event.GuavaEventBus;
import com.yunji.common.cache.RedisCache;
import com.yunji.common.constant.LogicCode;
import com.yunji.common.enums.business.StorageStatus;
import com.yunji.common.enums.business.StorageType;
import com.yunji.common.enums.common.ErrorEnum;
import com.yunji.common.event.notice.StockWarmNoticeEvent;
import com.yunji.common.model.dto.LoginInfo;
import com.yunji.common.utils.DecimalUtils;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.storage.bo.StockSelectQueryBO;
import com.yunji.storage.bo.StorageRecordsListQueryBO;
import com.yunji.storage.bo.StorageTicketSelectQueryBO;
import com.yunji.storage.business.StockBusiness;
import com.yunji.storage.consumer.StockConsumer;
import com.yunji.storage.domain.StockDO;
import com.yunji.storage.domain.StorageRecordsDO;
import com.yunji.storage.domain.StorageTicketDO;
import com.yunji.storage.dto.RollbackDTO;
import com.yunji.storage.dto.StockDTO;
import com.yunji.storage.dto.StorageOperateDTO;
import com.yunji.storage.service.*;
import com.yunji.system.context.SystemContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Project: yunji-coffee
 * @Package: com.yunji.storage.pattern
 * @Title: AbstractStorage
 * <p>
 * History:
 * Date                     Version     Author          Summary
 * ============================================================
 * 2024-08-22 15:51:23      V1.0        HuaAo       新建类
 */
public abstract class AbstractStorage {

    @Resource
    IStockService stockService;

    @Resource
    IStorageTicketService storageTicketService;

    @Resource
    IStorageRecordsService storageRecordsService;

    @Resource
    IAllocateTicketService allocateTicketService;

    @Resource
    IDiscardTicketService discardTicketService;

    @Resource
    IStockTakeService stockTakeService;

    @Resource
    IRepairTicketService repairTicketService;

    @Resource
    StockBusiness stockBusiness;

    @Resource
    StockConsumer stockConsumer;

    @Resource
    RedisCache redisCache;

    protected final Logger logs = LoggerFactory.getLogger(this.getClass());

    public static final String OPERATE_NUMS = "operate_nums";

    // 增加库存
    protected abstract void addStorage(StockDO stock);

    // 减少库存
    protected abstract void subtractStock(StockDO stock);

    // 返还库存
    protected abstract void backStock(StockDO stock);

    // 库存不存在时返还库存
    protected abstract void createBackStock(StockDO stock);

    // 更新业务单
    public abstract void updateTicket(Long businessId);

    // 查找业务单
    public abstract void checkTicket(String businessCode);


    /**
     * 出入库库存操作
     *
     * @param operateDTO
     */
    public void operateStorage(StorageOperateDTO operateDTO) {
        // 校验参数
        checkStorageParam(operateDTO);

        List<StockDO> addStock = new ArrayList<>();
        List<StockDO> subtractStock = new ArrayList<>();
        handleStockBusiness(operateDTO, addStock, subtractStock);

        // 参数处理
        Integer stockStatus = stockConsumer.getStockStatus(operateDTO.getBelongId());
        if (Objects.equals(LogicCode.YES, stockStatus) || (BeanUtils.isNull(operateDTO.getBusinessCode()))) {
            handleStock(addStock, subtractStock);

            // 处理预警消息
            handleMessage(operateDTO);
        }
    }

    /**
     * 取消业务
     *
     * @param dto
     */
    public void rollback(RollbackDTO dto) {
        //针对已经出库的业务单  未出库业务单是撤回操作

        Integer businessType = dto.getBusinessType();
        String businessCode = dto.getBusinessCode();
        Integer businessStatus = dto.getBusinessStatus();
        logs.info("【业务取消】业务类型:{}", businessType);
        logs.info("【业务取消】业务单号:{}", businessCode);
        logs.info("【业务取消】业务状态:{}", businessStatus);

        //处理出库单
        StorageTicketDO outTicket = storageTicketService.select(StorageTicketSelectQueryBO.builder()
                .businessCode(businessCode).storageType(StorageType.OUT_STORAGE.getCode()).build());
        Assert.isTrue(BeanUtils.isNotNull(outTicket), "未找到出库单");
        logs.info("【业务取消】出库单信息:{}", outTicket);

        String ticketCode = outTicket.getTicketCode();
        logs.info("【业务取消】出库单单号:{}", ticketCode);

        Long belongId = outTicket.getBelongId();
        logs.info("【业务取消】出库单所属单位:{}", belongId);

        StorageRecordsListQueryBO recordsParam = new StorageRecordsListQueryBO();
        recordsParam.setTicketCode(ticketCode);
        List<StorageRecordsDO> recordsList = storageRecordsService.search(recordsParam);

        BelongDO belong = BaseCache.getBelong(belongId);
        if (Objects.equals(LogicCode.YES, belong.getStockStatus())) {
            for (StorageRecordsDO records : recordsList) {

                Long productId = records.getProductId();
                logs.info("【业务取消】商品ID:{}", productId);

                BigDecimal actualQuantity = records.getActualQuantity();
                logs.info("【业务取消】已出库数量:{}", actualQuantity);
                //找到当前商品库存
                StockDO select = stockService.select(new StockSelectQueryBO(belongId, productId));

                if (BeanUtils.isNull(select)) {
                    StockDO create = new StockDO();
                    create.setProductId(productId);
                    create.setBelongId(belongId);
                    create.set(OPERATE_NUMS, actualQuantity);
                    createBackStock(create);
                } else {
                    select.set(OPERATE_NUMS, actualQuantity);
                    backStock(select);
                }
            }
        }

        //处理入库单  修改入库单状态
        StorageTicketDO intoTicket = storageTicketService.select(StorageTicketSelectQueryBO.builder()
                .businessCode(businessCode).storageType(StorageType.INTO_STORAGE.getCode()).build());
        Assert.isTrue(BeanUtils.isNotNull(intoTicket), "未找到入库单");
        logs.info("【业务取消】入库单信息:{}", intoTicket);

        Integer storageStatus = intoTicket.getStorageStatus();
        logs.info("【业务取消】入库单状态:{}", storageStatus);
        Assert.isTrue(Objects.equals(StorageStatus.WAIT_STORAGE.getCode(), storageStatus), "当前状态不支持取消");

        StorageTicketDO updateIntoTicket = new StorageTicketDO();
        updateIntoTicket.setTicketId(intoTicket.getTicketId());
        updateIntoTicket.setStorageStatus(StorageStatus.CANCEL_STORAGE.getCode());
        Assert.isTrue(storageTicketService.updateStorageTicket(updateIntoTicket), "取消失败");

        StorageTicketDO updateOutTicket = new StorageTicketDO();
        updateOutTicket.setTicketId(outTicket.getTicketId());
        updateOutTicket.setStorageStatus(StorageStatus.CANCEL_STORAGE.getCode());
        Assert.isTrue(storageTicketService.updateStorageTicket(updateOutTicket), "取消失败");

    }

    /**
     * @Title handleStock
     * @Desc 库存数量操作
     * @Date 2024-09-19 18:26:25.262
     */
    private void handleStock(List<StockDO> addStock, List<StockDO> subtractStock) {
        for (StockDO stock : addStock) {
            addStorage(stock);
        }

        for (StockDO stock : subtractStock) {
            subtractStock(stock);
        }

    }

    /**
     * @param operateDTO 业务DTO
     * @Title handleStockBusiness
     * @Desc 处理库存操作业务
     * @Date 2024-09-19 18:12:59.401
     */
    private void handleStockBusiness(StorageOperateDTO operateDTO, List<StockDO> addStock, List<StockDO> subtractStock) {
        //针对商品修改库存
        List<StockDTO> dtoList = operateDTO.getList();
        Long belongId = operateDTO.getBelongId();
        Integer storageType = operateDTO.getStorageType();
        Integer stockStatus = stockConsumer.getStockStatus(belongId);
        for (StockDTO stockDTO : dtoList) {
            Long productId = stockDTO.getProductId();
            logs.info("【库存操作】商品ID:{}", productId);

            BigDecimal actualQuantity = stockDTO.getActualQuantity();
            logs.info("【库存操作】商品数量:{}", actualQuantity);

            StockDO select = stockService.select(new StockSelectQueryBO(belongId, productId));

            StockDO stock = new StockDO();
            stock.set(OPERATE_NUMS, actualQuantity);
            // 1-开启库存开关
            // 2-没有进行业务操作
            if (Objects.equals(LogicCode.YES, stockStatus) || BeanUtils.isNull(operateDTO.getBusinessCode())) {
                if (BeanUtils.isNull(select)) {
                    stock.setBelongId(belongId);
                    stock.setProductId(productId);
                    Assert.isTrue(stockService.create(stock), "新增失败");
                } else {
                    BeanUtils.clone(select, stock);
                }
                if (Objects.equals(StorageType.INTO_STORAGE.getCode(), storageType)) {
                    addStock.add(stock);
                } else {
                    subtractStock.add(stock);
                }
            }

        }
    }

    /**
     * @param operateDTO 操作DTO
     * @Title handleMessage
     * @Desc 库存操作处理消息
     * @Date 2024-09-26 15:51:38.593
     */
    private void handleMessage(StorageOperateDTO operateDTO) {
        Long belongId = operateDTO.getBelongId();
        List<StockDTO> list = operateDTO.getList();

        for (StockDTO stockDTO : list) {
            Long productId = stockDTO.getProductId();

            StockDO stockVO = stockService.select(new StockSelectQueryBO(belongId, productId));

            BigDecimal warmValue = stockVO.getWarmValue();
            BigDecimal usableStock = stockVO.getUsableStock();
            logs.info("【预警消息】预警值:{},可用库存:{}", warmValue, usableStock);

            if (DecimalUtils.more(warmValue, usableStock)) {
                LoginInfo loginInfo = SystemContext.getUser();
                loginInfo.setBelongId(stockVO.getBelongId());
                GuavaEventBus.post(new StockWarmNoticeEvent(loginInfo,productId));
            }
        }
    }

    /**
     * @param operateDTO 库存操作参数
     * @Title checkStorageParam
     * @Desc 库存操作参数校验
     * @Date 2024-09-19 17:49:33.271
     */
    private void checkStorageParam(StorageOperateDTO operateDTO) {
        Long belongId = operateDTO.getBelongId();
        Integer storageType = operateDTO.getStorageType();
        Integer businessType = operateDTO.getBusinessType();
        List<StockDTO> dtoList = operateDTO.getList();
        logs.info("【库存操作】所属ID:{}", belongId);
        logs.info("【库存操作】出入库类型:{}", storageType);
        logs.info("【库存操作】业务类型:{}", businessType);
        logs.info("【库存操作】商品信息:{}", dtoList);
        Assert.isTrue(BeanUtils.isNotNull(belongId), ErrorEnum.BUSINESS_121005.getText());
        Assert.isTrue(BeanUtils.isNotNull(storageType), ErrorEnum.BUSINESS_812017.getText());
        Assert.isTrue(BeanUtils.isNotNull(storageType), ErrorEnum.BUSINESS_812018.getText());
        Assert.isTrue(BeanUtils.isNotNull(dtoList), ErrorEnum.BUSINESS_812019.getText());
    }

}